#pragma once

#include <cstdint>
#include <list>

#include "Misc/CommonMacros.h"
#include "Containers/Vector.h"
#include "String8.h"


// utf32 string
class WHENGINE_API  CString
{
protected:
    TVector<char32_t> Data;
public:
    CString();
    ~CString() {};

    CString(const char8_t* InUtf8String);

    CString(const char16_t* InUtf16String);

    CString(const char32_t* InUtf32String);

    CString(char32_t InUtf32Char);

    CString(int8_t InNum);

    CString(uint8_t InNum);

    CString(int16_t InNum);

    CString(uint16_t InNum);

    CString(int32_t InNum);

    CString(uint32_t InNum);

    CString(int64_t InNum);

    CString(uint64_t InNum); 

    CString(double InNum, int32_t Precision = 6 ) ;

    explicit CString(bool InValue);

    CString(const CString& Other);

    CString(const CString&& Other);

    CString& operator =(const CString& Other);

    CString& operator =(const CString&& Other);
    
    //base 10
    int64_t ToInt64() const;

    //base 10
    uint64_t ToUInt64() const;

    //this string only contains char '0'-'9'
    bool IsDigit() const;

    bool IsInt() const;

    bool IsFloat() const;

    //base 16
    uint64_t HexToUInt64() const;

    bool IsHex() const;

    //@ bWithPrefix : if true, add '0x' prefix
    static CString CreateHex(uint64_t InNum, bool bWithPrefix = false);

    //create a number string with base 10
    //@MinWidth , how many number characters at least, do not include '-'
    static CString CreateBase10(int64_t InNum, TOptional<int64_t> MinWidth = TOptional<int64_t>());

    double ToDouble() const;

    bool ToBool() const;

    bool operator ==(const CString& Other) const;

    //for std::map
    bool operator <(const CString& Other) const;

    uint64_t GetHashCode() const;

    //number of characters
    uint64_t GetLength() const;

    char32_t GetCharAt(uint64_t Index) const;

    //pre-allocated memory for characters
    //if the current capacity is less than InCharCount, the capacity will be increased to InCharCount
    void Reserve(uint64_t InCharCount);

    void Resize(uint64_t InCharCount);


    const char32_t* GetData() const;

    TVector<char32_t> GetUnicodes() const;


    CString operator +(const CString& Other) const;

    CString& operator +=(const CString& Other);

    CString& operator +=(char32_t Other);

    const char32_t& operator [](uint64_t Index) const;

    char32_t& operator [](uint64_t Index);
    
    bool IsEmpty() const;

    void Clear();

    bool StartsWith(const CString& Other) const;

    bool EndsWith(const CString& Other) const;

    static CString Join(const std::list<CString>& Strings);

    CString SubString(uint64_t StartIndex, uint64_t Length) const;

    CString SubString(uint64_t StartIndex) const;

    CString Left(uint64_t Length) const;

    CString Right(uint64_t Length) const;

    CString Insert(uint64_t Index, const CString& Other ) const ;

    //return the index of the first character of the first occurrence of Other in this string
    //return -1 if not found
    int64_t Find(const CString& Other, uint64_t StartIndex = 0) const;

    //return the index of the first character of the last occurrence of Other in this string
    //return -1 if not found
    int64_t FindLast(const CString& Other) const;

    //return the index of the first character of the first occurrence of Other in this string
    //return -1 if not found
    int64_t Find(char32_t Other, uint64_t StartIndex = 0) const;

    //return the index of the first character of the last occurrence of Other in this string
    //return -1 if not found
    int64_t FindLast(char32_t Other) const;

    //return count of Other in this string
    int64_t Count(const CString& Other) const;

    //return if this string contains any character in Other
    bool ContainsAnyOf(const CString& Other) const;

    //return if this string contains all characters in Other
    bool Contains(const CString& Other) const;

    CString ToLower() const;

    CString ToUpper() const;

    TVector<CString> Split(const CString& Delimiter) const;

    TVector<CString> Split(char32_t Delimiter) const;

    TVector<CString> SplitByMultiple(const TVector<CString>& Delimiters) const;

    //remove blank space at the beginning and the end of the string
    CString Trim() const;

    CString Replace(const CString& From, const CString& To) const;

    CString Replace(char32_t From, char32_t To) const;

    //remove all occurrences of the character
    CString Remove(char32_t Char) const;

    //remove the character at the index
    CString RemoveAt(uint64_t Index, uint32_t Length = 1) const;

    //remove all occurrences of the string
    CString Remove(const CString& Str) const;

    //repeat this string for Count times
    //if Count is 0, return empty string
    //if Count is 1, return this string
    CString Repeat(uint64_t Count) const;

    static TVector<char32_t> ToUtf32(const char8_t* InUtf8String);

    static TVector<char32_t> ToUtf32(const char16_t* InUtf16String);

    //for save to file / send to network / print to console
    //for console out example ,
    TVector<char8_t> ToUtf8() const ;

    CString8 ToUtf8String() const;

    //window api use utf16
    TVector<char16_t> ToUtf16() const;

    friend WHENGINE_API CString operator + (const char32_t* Left, const CString& Right);
    friend WHENGINE_API CString operator + (char32_t Left, const CString& Right);
};


 WHENGINE_API CString operator + (const char32_t* Left, const CString& Right);

 WHENGINE_API CString operator + (char32_t Left, const CString& Right);

#ifdef USING_SOURCE_LOCATION
#include <source_location>
#endif

namespace WH
{

   

    WHENGINE_API CString ToString(const CString& InValue);

#ifdef USING_SOURCE_LOCATION
#include <source_location>
    WHENGINE_API CString ToString(const std::source_location& InSourceLocation);
#endif

    WHENGINE_API CString ToString(bool InValue);

    WHENGINE_API CString ToString(int8_t InValue);

    WHENGINE_API CString ToString(uint8_t InValue);

    WHENGINE_API CString ToString(int16_t InValue);

    WHENGINE_API CString ToString(uint16_t InValue);

    WHENGINE_API CString ToString(int32_t InValue);

    WHENGINE_API CString ToString(uint32_t InValue);

    WHENGINE_API CString ToString(int64_t InValue);

    WHENGINE_API CString ToString(uint64_t InValue);

    WHENGINE_API CString ToString(float InValue, int32_t Precision = 6);

    WHENGINE_API CString ToString(double InValue, int32_t Precision = 6);

}